/*global pi */

/**
 * 本文件定义了4*4的矩阵及其相关的处理函数
 * 注意：在这里是用列向量相乘的矩阵，
 * 矩阵m和向量v相乘：m * v，v是列向量:
 * [ m[ 0]  m[ 1]  m[ 2]  m[ 3] ]   {v[0]}
 * | m[ 4]  m[ 5]  m[ 6]  m[ 7] | * {v[1]}
 * | m[ 8]  m[ 9]  m[10]  m[11] |   {v[2]}
 * [ m[12]  m[13]  m[14]  m[15] ]   {1}
 *
 * 转OpenGL矩阵的时候，需要将m转置过来(OpenGL虽然也是和列向量相乘，不过OpenGL的矩阵也是列优先)
 * 另，CVV用的是OpenGL的范围：-1 <= x <= 1, -1 <= y <= 1, -1 <= z <= 1
 */

pi.mod.define('pi.math.mat4', function (pi) {
	"use strict";
	var module = {},
		tmpV1 = pi.math.vec3.create(),
		tmpV2 = pi.math.vec3.create(),
		tmpV3 = pi.math.vec3.create();

	// 零矩阵
	module.ZERO = (function () {
		var m = pi.lang.createF32Array(16);
		m[0] = m[1] = m[2] = m[3] = 0;
		m[4] = m[5] = m[6] = m[7] = 0;
		m[8] = m[9] = m[10] = m[11] = 0;
		m[12] = m[13] = m[14] = m[15] = 0;
		return m;
	}());

	// 单位阵
	module.IDENTITY = (function () {
		var m = pi.lang.createF32Array(16);
		m[1] = m[2] = m[3] = 0.0;
		m[4] = m[6] = m[7] = 0.0;
		m[8] = m[9] = m[11] = 0.0;
		m[12] = m[13] = m[14] = 0.0;
		m[0] = m[5] = m[10] = m[15] = 1.0;
		return m;
	}());

	// 创建
	module.create = function () {
		return pi.lang.createF32Array(16);
	};

	// 拷贝
	module.copy = function (dst, src) {
		dst[0] = src[0];
		dst[1] = src[1];
		dst[2] = src[2];
		dst[3] = src[3];

		dst[4] = src[4];
		dst[5] = src[5];
		dst[6] = src[6];
		dst[7] = src[7];

		dst[8] = src[8];
		dst[9] = src[9];
		dst[10] = src[10];
		dst[11] = src[11];

		dst[12] = src[12];
		dst[13] = src[13];
		dst[14] = src[14];
		dst[15] = src[15];
	};

	// 是否相等
	module.isEqual = function (m1, m2) {
		var i, tmp;
		for (i = 0; i < 16; ++i) {
			tmp = Math.abs(m1[i] - m2[i]);
			if (tmp !== Number.MIN_VALUE) {
				return false;
			}
		}
		return true;
	};

	// 转置
	module.transpose = function (dst, src) {
		dst[0] = src[0];
		dst[1] = src[4];
		dst[2] = src[8];
		dst[3] = src[12];
		dst[4] = src[1];
		dst[5] = src[5];
		dst[6] = src[9];
		dst[7] = src[13];
		dst[8] = src[2];
		dst[9] = src[6];
		dst[10] = src[10];
		dst[11] = src[14];
		dst[12] = src[3];
		dst[13] = src[7];
		dst[14] = src[11];
		dst[15] = src[15];
	};

	// 乘法
	module.mul = function (m, m1, m2) {
		m[0] = m1[0] * m2[0] + m1[1] * m2[4] + m1[2] * m2[8] + m1[3] * m2[12];
		m[1] = m1[0] * m2[1] + m1[1] * m2[5] + m1[2] * m2[9] + m1[3] * m2[13];
		m[2] = m1[0] * m2[2] + m1[1] * m2[6] + m1[2] * m2[10] + m1[3] * m2[14];
		m[3] = m1[0] * m2[3] + m1[1] * m2[7] + m1[2] * m2[11] + m1[3] * m2[15];

		m[4] = m1[4] * m2[0] + m1[5] * m2[4] + m1[6] * m2[8] + m1[7] * m2[12];
		m[5] = m1[4] * m2[1] + m1[5] * m2[5] + m1[6] * m2[9] + m1[7] * m2[13];
		m[6] = m1[4] * m2[2] + m1[5] * m2[6] + m1[6] * m2[10] + m1[7] * m2[14];
		m[7] = m1[4] * m2[3] + m1[5] * m2[7] + m1[6] * m2[11] + m1[7] * m2[15];

		m[8] = m1[8] * m2[0] + m1[9] * m2[4] + m1[10] * m2[8] + m1[11] * m2[12];
		m[9] = m1[8] * m2[1] + m1[9] * m2[5] + m1[10] * m2[9] + m1[11] * m2[13];
		m[10] = m1[8] * m2[2] + m1[9] * m2[6] + m1[10] * m2[10] + m1[11] * m2[14];
		m[11] = m1[8] * m2[3] + m1[9] * m2[7] + m1[10] * m2[11] + m1[11] * m2[15];

		m[12] = m1[12] * m2[0] + m1[13] * m2[4] + m1[14] * m2[8] + m1[15] * m2[12];
		m[13] = m1[12] * m2[1] + m1[13] * m2[5] + m1[14] * m2[9] + m1[15] * m2[13];
		m[14] = m1[12] * m2[2] + m1[13] * m2[6] + m1[14] * m2[10] + m1[15] * m2[14];
		m[15] = m1[12] * m2[3] + m1[13] * m2[7] + m1[14] * m2[11] + m1[15] * m2[15];
	};

	// 逆矩阵
	module.inverse = function (dst, src) {
		var v0 = src[8] * src[13] - src[9] * src[12],
			v1 = src[8] * src[14] - src[10] * src[12],
			v2 = src[8] * src[15] - src[11] * src[12],
			v3 = src[9] * src[14] - src[10] * src[13],
			v4 = src[9] * src[15] - src[11] * src[13],
			v5 = src[10] * src[15] - src[11] * src[14],
			t00 = v5 * src[5] - v4 * src[6] + v3 * src[7],
			t10 = -(v5 * src[4] - v2 * src[6] + v1 * src[7]),
			t20 = v4 * src[4] - v2 * src[5] + v0 * src[7],
			t30 = -(v3 * src[4] - v1 * src[5] + v0 * src[6]),
			invDet = 1.0 / (t00 * src[0] + t10 * src[1] + t20 * src[2] + t30 * src[3]);

		dst[0] = t00 * invDet;
		dst[4] = t10 * invDet;
		dst[8] = t20 * invDet;
		dst[12] = t30 * invDet;

		dst[1] = -(v5 * src[1] - v4 * src[2] + v3 * src[3]) * invDet;
		dst[5] = +(v5 * src[0] - v2 * src[2] + v1 * src[3]) * invDet;
		dst[9] = -(v4 * src[0] - v2 * src[1] + v0 * src[3]) * invDet;
		dst[13] = +(v3 * src[0] - v1 * src[1] + v0 * src[2]) * invDet;

		v0 = src[4] * src[13] - src[5] * src[12];
		v1 = src[4] * src[14] - src[6] * src[12];
		v2 = src[4] * src[15] - src[7] * src[12];
		v3 = src[5] * src[14] - src[6] * src[13];
		v4 = src[5] * src[15] - src[7] * src[13];
		v5 = src[6] * src[15] - src[7] * src[14];

		dst[2] = +invDet * (v5 * src[1] - v4 * src[2] + v3 * src[3]);
		dst[6] = -invDet * (v5 * src[0] - v2 * src[2] + v1 * src[3]);
		dst[10] = +invDet * (v4 * src[0] - v2 * src[1] + v0 * src[3]);
		dst[14] = -invDet * (v3 * src[0] - v1 * src[1] + v0 * src[2]);

		v0 = src[9] * src[4] - src[8] * src[5];
		v1 = src[10] * src[4] - src[8] * src[6];
		v2 = src[11] * src[4] - src[8] * src[7];
		v3 = src[10] * src[5] - src[9] * src[6];
		v4 = src[11] * src[5] - src[9] * src[7];
		v5 = src[11] * src[6] - src[10] * src[7];

		dst[3] = -invDet * (v5 * src[1] - v4 * src[2] + v3 * src[3]);
		dst[7] = invDet * (v5 * src[0] - v2 * src[2] + v1 * src[3]);
		dst[11] = -invDet * (v4 * src[0] - v2 * src[1] + v0 * src[3]);
		dst[15] = invDet * (v3 * src[0] - v1 * src[1] + v0 * src[2]);
	};

	// 作用于点：平移分量起作用
	module.applyPt = function (dst, m, p) {
		var fInvW = 1.0 / (m[15] + m[12] * p.x + m[13] * p.y + m[14] * p.z);
		dst.x = fInvW * (m[3] + p.x * m[0] + p.y * m[1] + p.z * m[2]);
		dst.y = fInvW * (m[7] + p.x * m[4] + p.y * m[5] + p.z * m[6]);
		dst.z = fInvW * (m[11] + p.x * m[8] + p.y * m[9] + p.z * m[10]);
	};

	// 作用于向量：平移分量不起作用
	module.applyVec = function (dst, m, p) {
		dst.x = p.x * m[0] + p.y * m[1] + p.z * m[2];
		dst.y = p.x * m[4] + p.y * m[5] + p.z * m[6];
		dst.z = p.x * m[8] + p.y * m[9] + p.z * m[10];
	};

	// 构造平移矩阵
	module.translate = function (dst, tVec) {
		dst[3] = tVec.x;
		dst[7] = tVec.y;
		dst[11] = tVec.z;
		dst[1] = dst[2] = 0.0;
		dst[4] = dst[6] = 0.0;
		dst[8] = dst[9] = 0.0;
		dst[12] = dst[13] = dst[14] = 0.0;
		dst[0] = dst[5] = dst[10] = dst[15] = 1.0;
	};

	// 构造缩放矩阵
	module.scale = function (dst, sVec) {
		dst[0] = sVec.x;
		dst[5] = sVec.y;
		dst[10] = sVec.z;
		dst[15] = 1.0;
		dst[1] = dst[2] = dst[3] = 0.0;
		dst[4] = dst[6] = dst[7] = 0.0;
		dst[8] = dst[9] = dst[11] = 0.0;
		dst[12] = dst[13] = dst[14] = 0.0;
	};

	/**
	 * 构造旋转矩阵
	 * rQuat: pi.math.quat, 旋转四元数
	 */
	module.rotate = function (dst, rQuat) {
		var fTx = rQuat.x + rQuat.x,
			fTy = rQuat.y + rQuat.y,
			fTz = rQuat.z + rQuat.z,
			fTwx = fTx * rQuat.w,
			fTwy = fTy * rQuat.w,
			fTwz = fTz * rQuat.w,
			fTxx = fTx * rQuat.x,
			fTxy = fTy * rQuat.x,
			fTxz = fTz * rQuat.x,
			fTyy = fTy * rQuat.y,
			fTyz = fTz * rQuat.y,
			fTzz = fTz * rQuat.z;
		dst[0] = 1.0 - (fTyy + fTzz);
		dst[1] = fTxy - fTwz;
		dst[2] = fTxz + fTwy;
		dst[4] = fTxy + fTwz;
		dst[5] = 1.0 - (fTxx + fTzz);
		dst[6] = fTyz - fTwx;
		dst[8] = fTxz - fTwy;
		dst[9] = fTyz + fTwx;
		dst[10] = 1.0 - (fTxx + fTyy);
		dst[15] = 1.0;
		dst[3] = dst[7] = dst[11] = 0.0;
		dst[12] = dst[13] = dst[14] = 0.0;
	};

	/**
	 * 根据translate、rotate、scale构造出变换矩阵
	 * 注意约定的顺序：先scale，后rotate，最后做translate
	 */
	module.transform = function (dst, tVec, sVec, rQuat) {
		module.rotate(dst, rQuat);
		dst[0] *= sVec.x;
		dst[1] *= sVec.y;
		dst[2] *= sVec.z;
		dst[4] *= sVec.x;
		dst[5] *= sVec.y;
		dst[6] *= sVec.z;
		dst[8] *= sVec.x;
		dst[9] *= sVec.y;
		dst[10] *= sVec.z;
		dst[3] = tVec.x;
		dst[7] = tVec.y;
		dst[11] = tVec.z;
	};

	// 构造右手坐标系的视图矩阵
	module.lookat = function (dst, posVec, targetVec, upVec) {
		pi.math.vec3.sub(tmpV3, posVec, targetVec);
		pi.math.vec3.normalize(tmpV3, tmpV3);

		// zAxis要不为0向量
		pi.math.vec3.cross(tmpV1, upVec, tmpV3);
		pi.math.vec3.normalize(tmpV1, tmpV1);

		// xAxis要不为0向量
		pi.math.vec3.cross(tmpV2, tmpV3, tmpV1);

		dst[0] = tmpV1.x;
		dst[4] = tmpV2.x;
		dst[8] = tmpV3.x;

		dst[1] = tmpV1.y;
		dst[5] = tmpV2.y;
		dst[9] = tmpV3.y;

		dst[2] = tmpV1.z;
		dst[6] = tmpV2.z;
		dst[10] = tmpV3.z;

		dst[3] = -pi.math.vec3.dot(tmpV1, posVec);
		dst[7] = -pi.math.vec3.dot(tmpV2, posVec);
		dst[11] = -pi.math.vec3.dot(tmpV3, posVec);

		dst[15] = 1.0;
		dst[12] = dst[13] = dst[14] = 0.0;
	};

	/**
	 * 构造右手系的透视投影矩阵
	 * lt、rt、bm、tp为近裁剪面的矩阵的左、右、上、下边界的数值
	 * 注意：由于透视矩阵的照相机位置不能位于裁剪体内部，所以zn和zf必须取正值，
	 * 而且zn越少，近景的误差越大
	 */
	module.frustum = function (dst, lt, rt, bm, tp, zn, zf) {
		var dx = 1.0 / (rt - lt),
			dy = 1.0 / (tp - bm),
			dz = 1.0 / (zn - zf);
		dst[0] = 2 * zn * dx;
		dst[5] = 2 * zn * dy;
		dst[2] = dx * (rt + lt);
		dst[6] = dy * (bm + tp);
		dst[10] = dz * (zf + zn);
		dst[14] = -1;
		dst[11] = 2 * dz * zn * zf;
		dst[1] = dst[3] = 0.0;
		dst[4] = dst[7] = 0.0;
		dst[8] = dst[9] = 0.0;
		dst[12] = dst[13] = dst[15] = 0.0;
	};

	/**
	 * 构造一个右手系的透视投影矩阵
	 * fovRad: yz方向的视野角度，单位弧度
	 * zNear、zFar，近远裁剪面到相机的距离，均为正数，而且zNear不能等于zFar
	 * ratio：宽高比
	 */
	module.perspective = function (dst, fovRad, ratio, zNear, zFar) {
		var h = zNear * Math.tan(0.5 * fovRad),
			w = h * ratio;
		module.frustum(dst, -w, w, -h, h, zNear, zFar);
	};

	/**
	 * 构造一个右手系的正交投影矩阵
	 * zNear和zFar可正可负，但zNear和zfar不能相等
	 * zNear 表示z值的最小值
	 * zFar 表示z值的最大值
	 */
	module.ortho = function (dst, lt, rt, bm, tp, zn, zf) {
		var dx = 1.0 / (rt - lt),
			dy = 1.0 / (tp - bm),
			dz = 1.0 / (zn - zf);
		dst[0] = 2 * dx;
		dst[5] = 2 * dy;
		dst[10] = 2 * dz;
		dst[3] = -dx * (lt + rt);
		dst[7] = -dy * (bm + tp);
		dst[11] = dz * (zn + zf);
		dst[15] = 1.0;

		dst[1] = dst[2] = 0.0;
		dst[4] = dst[6] = 0.0;
		dst[8] = dst[9] = 0.0;
		dst[12] = dst[13] = dst[14] = 0.0;
	};

	return module;
});